#include <sstream>
#include <string>
#include <set>
#include <vector>
#include <map>

#include <iostream>

#ifndef __FILE_TYPES_H

// основные типы данных

// span - диапазон знаков в строке:
//   pos - номер первого знака
//   len - количество знаков
struct span {
  size_t pos, len;
  span() : pos(0), len(0) { }
  span(size_t p_, size_t l_) : pos(p_), len(l_) { }
};

// gramset - набор грамматических признаков
// грамматические признаки обозначаются строками
struct gramset {
  std::set<std::string> gram;
};

// gramword - слово + его грамматические признаки
struct gramword {
  std::string t;
  gramset g;
};

// form - форма слова
//   word - его текстовое представление + общие для всех вариантов грамматические признаки
//   gram - вектор наборов различающихся для разных вариантов грамматических признаков
struct form {
  gramword word;
  std::vector<gramset> gram;

  bool has_lemma_gram(const std::string &g) const {
    return word.g.gram.end() != word.g.gram.find(g);
  }

  bool has_form_gram(const std::string &g) const {
    // TODO: проверить, нет ли такой грамемы у одной из форм
    return false;
  }

  bool has_gram(const std::string &g) const {
    return has_lemma_gram(g) || has_form_gram(g);
  }
};

// morph - результат морфологического разбора:
//
//   Например (для слова "стекло"):
//   "стечь",  {VERB, perf}
//                    {past, neut, 3per}
//   "стекло", {NOUN, neut}
//                    {sing, nomn}
//                    {sing, accs}
struct morph {
  morph() { }
  morph(const std::string &f, const std::string &gs) {
    // TODO: инициализировать используя эти аргументы:
    // f - лемма
    // gs - набор грамматических признаков через пробел (hint: нужно нарезать строку по пробелу)
  }

  std::vector<form> m;
};

// group - группа слов (синтаксическая группа)
// s - диапазон слов в группе
// mw - номер главного слова в группе (считаем от начала группы, т.е. первое слово в группе имеет номер 0
// m - морфологические варианты, с которыми группа собралась
struct group {
  span s; // это токен-спан, т.е. тут номера токенов, а не знаков
  size_t mw;
  std::map<size_t, std::pair<size_t, size_t> > m; // выбранные варианты грамматического описания 
  // номер слова -> пара < номер леммы, номер набора грамматических признаков >
}; 

// text - текст с разбиением на слова и морфологическим разбором
struct text {
  std::string s;
  std::vector<span> tokens;
  std::vector<morph> words;
  std::vector<group> groups;

  std::string toString() const {
    std::stringstream ss;
    ss << "text: \"" << s << "\"" << std::endl;
    for (size_t i = 0; i < tokens.size(); ++i )
      ss << "[" << tokens[i].pos << ", " << tokens[i].len << "]  ";
    ss << std::endl;
    // TODO: Дальше выводим words и groups ...
    return ss.str();
  }
};

// абстрактный класс, описывающий интерфейс сегментатора на слова (токенизатора)
// возвращает разбиение её на слова
class ITokenizer {
public:

  // функция принимает строку и делит на слова
  virtual std::vector<span> Tokenize(std::string s) const = 0;
};

// абстрактный класс, описывающий интерфейс морфологического аналаизатора
// принимает строку и разбиение её на слова
// возвращает морфологический разбор
class IMorphology {
public:

  // Функция проводит анализ всех слов текста
  virtual std::vector<morph> Analyze(const std::string &s, const std::vector<span> &t) const = 0;

  // Функция ищет морф. интерпретацию одного слова
  virtual morph Lookup(const std::string &s) const = 0;
};

// абстрактный класс, описывающий интерфейс синтаксического аналиатора
class ISynan {
public:

  // Функция ищет все возможные синтаксические группы (в том числе и перекрывающиеся)
  virtual std::vector<group> Analyze(const std::vector<morph> &w) const = 0;
};

#endif
#define __FILE_TYPES_H
